package main

import "fmt"

// ==========运算符==========
// 1、算术运算符
// 2、赋值运算符
// 3、比较运算符
// 4、逻辑运算符
// 5、位运算符
// 6、其他运算符

func swap(a, b *int) {
	// 取a指针的值, 赋给临时变量t
	t := *a
	// 取b指针的值, 赋给a指针指向的变量
	*a = *b
	// 将a指针的值赋给b指针指向的变量
	*b = t
}

func main() {
	// 算术运算符
	// -------------------------------------
	// 运算符		运算			范例
	// +			正号			+3
	// -			负号			-3
	// +			加			1 + 1
	// -			减			2 - 1
	// *			乘			3 * 4
	// /			除			5 / 3
	// %			取模			7 % 5
	// ++			自增			a = 2 a++
	// --			自减			a = 1 a--
	// +			字符串相加	"He" + "llo"
	// -------------------------------------
	// 如果运算的数都是整数, 除法的结果去除小数部分, 仅保留整数部分
	fmt.Println(10 / 4) // 2
	// 如果要保留小数部分, 则需要浮点数参与运算
	fmt.Println(10.0 / 4) // 2.5
	// 取模的本质: a % b = a - a / b * b
	// 如果a > 0, 则a % b = |a| % |b|
	// 如果a < 0, 则a % b = - |a| % |b|
	fmt.Println("10 % 3 =", 10%3)     // 1
	fmt.Println("-10 % 3 =", -10%3)   // -1
	fmt.Println("10 % -3 =", 10%-3)   // 1
	fmt.Println("-10 % -3 =", -10%-3) // -1
	// ++和--的使用, ++和--只能单独使用
	i := 1
	fmt.Println("i自增前等于", i) // 1
	i++                      // 等价于 i += 1
	fmt.Println("i自增后等于", i) // 2
	j := 2
	fmt.Println("j自减前等于", j) // 2
	j--                      // 等价于 j -= 1
	fmt.Println("j自减后等于", j) // 1

	// 比较运算符(关系运算符)
	// 要么是true, 要么是false, 包括: ==、!=、>、>=、<、<=

	// 逻辑运算符
	// 逻辑与: &&
	// 逻辑或: ||
	// 逻辑非: !

	// 赋值运算符, 将某个运算后的值, 赋给一个变量
	// 1) 赋值运算的顺序是从右向左
	// 2) 赋值运算符的左边只能是变量, 右边可以是变量、常量、表达式
	// 3) 复合赋值运算的效果等价于, a += 1 <==> a = a + 1
	// ----------------------------
	// 运算符		描述
	// =           简单的赋值运算符
	// +=			相加后再赋值
	// -=			相减后再赋值
	// *=			相乘后再赋值
	// /=			相除后再赋值
	// %=			求余后再赋值
	// <<=			左移后再赋值
	// >>=			右移后再赋值
	// &=			按位与后赋值
	// |=			按位或后赋值
	// ^=			按位异或后赋值
	// ----------------------------

	// 交换两个变量
	var a, b = 1, 2
	// 方式1
	var k int
	k = a
	a = b
	b = k
	fmt.Println(a, b) // 2 1
	// 方式2
	a, b = b, a
	fmt.Println(a, b) // 1 2
	// 方式3
	a = a + b
	b = a - b
	a = a - b
	fmt.Println(a, b) // 2 1
	// 方式4
	var ptr1 = &a
	var ptr2 = &b
	swap(ptr1, ptr2)

	// 位运算符
	/*
	   原码、反码、补码

	   对于有符号的整数而言
	   1)二进制的最高位是符号位: 0表示正数、1表示负数
	   2)负数的反码=它的原码符号位不变, 其它位取反
	   3)负数的补码=它的反码+1
	   4)0的反码、补码都是0
	   5)在计算机运算的时候, 都是以补码的方式来运算的

	   对于无符号的整数而言
	   1)原码、反码、补码都一样

	   举例说明
	   1 --> 原码[0000 0001] 反码[0000 0001] 补码[0000 0001]
	   -1 -> 原码[1000 0001] 反码[1111 1110] 补码[1111 1111]
	*/

	// 按位与x&y, bit都为1的时候结果为1, 其余都等于0
	// 2: 0000 0010
	// 3: 0000 0011
	// 2 & 3: 0000 0010
	fmt.Println(2 & 3) // 2

	// 按位或x|y, bit有一个为1结果就为1
	// 2: 0000 0010
	// 3: 0000 0011
	// 2 & 3: 0000 0011
	fmt.Println(2 | 3) // 3

	// 按位异或x^y, bit不相同时为1
	// 2: 0000 0010
	// 3: 0000 0011
	// 2 & 3: 0000 0001
	fmt.Println(2 ^ 3) // 1

	// 正数与负数求异或 2 ^ -2
	// -2的原码[1000 0010]
	// -2的反码[1111 1101]
	// -2的补码[1111 1110]
	// 2的补码[0000 0010]
	// -2 ^ 2 就是用-2的补码和2的补码计算
	// 1111 1110 ^ 0000 0010 = 1111 1100(补码)
	// 1111 1100的反码是1111 1011
	// 1111 1011的原码就是1000 0100
	// 最终打印的是原码-4
	fmt.Println(-2 ^ 2) // -4

	// 移位运算符(也是用补码运算, 只要是运算都是补码)
	// 1)右移运算符>>: 低位溢出, 符号位不变, 并用符号位补溢出的高位
	// 2)左移运算符<<: 符号位不变, 低位补0

	// 0000 0001 --> 0000 0000
	x := 1 >> 2
	fmt.Println(x) // 0

	// 0000 0001 --> 0000 0100
	y := 1 << 2
	fmt.Println(y) // 4

	// -1的原码[1000 0001]
	// -1的反码[1111 1110]
	// -1的补码[1111 1111]
	// -1的补码[1111 1111]右移2位变成[1111 1111]
	// 通过[1111 1111]求出反码是[1111 1110]
	// 通过[1111 1110]求出原码是[1000 0001]
	// 原码[1000 0001]代表的十进制就是-1, 所以最后的结果就是-1
	// -1右移任何位结果还是-1
	c := -1 >> 2
	fmt.Println(c)

	// -10的原码是[1000 1010]
	// -10的反码是[1111 0101]
	// -10的补码是[1111 0110]
	// -10的补码[1111 0110]右移2位变成[1111 1101]
	// 通过[1111 1101]求出反码是[1111 1100]
	// 通过[1111 1100]求出原码是[1000 0011]
	// [1000 0011]对应的十进制就是-3, 所以结果就是-3
	fmt.Println(-10 >> 2)

	// 按位取反^x
	// 所有正整数的按位取反是其本身+1的负数
	// 所有负整数的按位取反是其本身+1的绝对值
	// 零的按位取反是 -1
	// 0000 0100   4 原码
	// 0000 0100   4 补码
	// 1111 1011   4的补码取反记为 x
	// 1111 1010   x-1 得到反码记为 y
	// 1111 0101   y 取反得到源码，此为最终结果 -5
	fmt.Println(^4)

	// 只有单目运算符和赋值运算符是从右向左的, 其余运算符都是从左向右

	// 其他进制转十进制
	// 1) 二进制转十进制
	// 1011 = 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 11
	// 2) 八进制转十进制
	// 0123 = 0 * 8^3 + 1 * 8^2 + 2 * 8^1 + 3 * 8^0 = 83
	// 3) 十六进制转十进制
	// 0x34A = 3 * 16^2 + 4 * 16^1 + 10 = 842

	// 十进制转其他进制
	// 1) 十进制转二进制
	// 规则: 将该数不断除以2, 直到商为0为止, 然后将每步得到的余数倒过来, 就是对应的二进制
	/*
		十进制的56转二进制演示: 56 --> 111000
		2 | 56  0
		   ---
		2 | 28  0
		   ---
		2 | 14  0
		   ---
		2 | 7   1
		   ---
		2 | 3   1
		   ---
		2 | 1   1
		   ---
		    0
	*/

	// 2) 十进制转八进制
	// 规则: 将该数不断除以8, 直到商为0为止, 然后将每步得到的余数倒过来, 就是对应的八进制
	/*
		十进制的156转八进制演示: 156 --> 0234
		8 | 156  4
		   ----
		8 | 19   3
		   ----
		8 | 2    2
		   ----
		    0
	*/

	// 3) 十进制转十六进制
	// 规则: 将该数不断除以16, 直到商为0为止, 然后将每步得到的余数倒过来, 就是对应的十六进制
	/*
		十进制的356转八进制演示: 356 --> 0x164
		16 | 356  4
			----
		16 | 22   6
			----
		16 | 1    1
			----
			 0
	*/

	// 二进制与八进制、十六进制的转换
	// 1) 二进制转八进制
	// 规则: 将二进制每三位一组(从低位开始组合), 转成对应的八进制数即可
	// 案例: 11010101 = 0325

	// 2) 二进制转十六进制
	// 规则: 将二进制每四位一组(从低位开始组合), 转成对应的十六进制数即可
	// 案例: 11010101 = 0xD5

	// 3) 八进制转二进制
	// 规则: 将八进制数每一位, 转成对应的一个3位的二进制数即可
	// 案例: 0237 = 10011111

	// 4) 十六进制转二进制
	// 规则: 将十六进制数每一位, 转成对应的一个4位的二进制数即可
	// 案例: 0x237 = 1000110111
}
